using System;
using System.Collections;
using System.Diagnostics.Contracts;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace RandomSearch.PluginsCommon {
    public static class FSACLHelper {
        public static bool CanReadDirectory(string directory, Func<IdentityReference, bool> isForCurrentIdentity) {
            return CanReadDirectory(new DirectoryInfo(directory), isForCurrentIdentity);
        }

        public static bool CanReadDirectory(DirectoryInfo directory, Func<IdentityReference, bool> isForCurrentIdentity) {
            Contract.Assert(null != isForCurrentIdentity);
            Contract.Assert(null != directory);

            DirectorySecurity access;
            try {
                access = directory.GetAccessControl();
            }
                //Don't have right to read AC
            catch (UnauthorizedAccessException) {
                return false;
            }
            AuthorizationRuleCollection rules = access.GetAccessRules(true, true, typeof (SecurityIdentifier));
            return IsReadAllowed(rules, isForCurrentIdentity);
        }

        public static bool CanReadFile(string file, Func<IdentityReference, bool> isForCurrentIdentity) {
            return CanReadFile(new FileInfo(file), isForCurrentIdentity);
        }

        public static bool CanReadFile(FileInfo file, Func<IdentityReference, bool> isForCurrentIdentity) {
            Contract.Assert(null != isForCurrentIdentity);
            Contract.Assert(null != file);
            FileSecurity access;
            try {
                access = file.GetAccessControl();
            }
            catch (UnauthorizedAccessException) {
                return false;
            }
            catch (InvalidOperationException) {
                return false;
            }
            AuthorizationRuleCollection rules = access.GetAccessRules(true, true, typeof (SecurityIdentifier));
            return IsReadAllowed(rules, isForCurrentIdentity);
        }

        private static bool IsReadAllowed(AuthorizationRuleCollection rules,
                                          Func<IdentityReference, bool> isForCurrentIdentity) {
            bool allowed = false, denied = false;
            const FileSystemRights fsr = FileSystemRights.ReadData;
            for (IEnumerator e = rules.GetEnumerator(); e.MoveNext();) {
                var ar = e.Current as FileSystemAccessRule;
                if (isForCurrentIdentity(ar.IdentityReference)) {
                    if (AccessControlType.Deny == ar.AccessControlType)
                        denied = denied || ((ar.FileSystemRights & fsr) == fsr);
                    else
                        allowed = allowed || ((ar.FileSystemRights & fsr) == fsr);
                }
            }
            return allowed && !denied;
        }
    }
}